/*!
 * 将平铺的文件数组数据转换成树形结构，clone自 https://github.com/nknapp/tree-from-paths
 *
 * tree-from-paths <https://github.com/nknapp/tree-from-paths>
 *
 * Copyright (c) 2017 Nils Knappmeier.
 * Released under the MIT license.
 */

import groupBy from 'lodash.groupby';
import { YunxiaoIcon } from '@alife/devops-ui';

/**
 * Transform an array of paths into an [archy](https://www.npmjs.com/package/archy)-compatible tree structure.
 *
 * ```
 * ['abc/cde/efg/', 'abc/cde/abc', 'abc/zyx']
 * ```
 * becomes
 * ```
 * {
 *   label: 'abc/',
 *   nodes: [{ label: 'cde/',nodes: ['efg/', 'abc']}, 'zyx']
 * }
 * ```
 *
 * Nodes with a single subnode are collapsed and the resulting node gets the label `node/subnode`.
 *
 * @param {string[]} files an array of sorted file paths relative to `parent`
 * @param {string} baseDir the directory of the files
 * @param {function(parent:string, file:string, explicit: boolean):string} renderLabelFn function that renders the label
 *  of a node. It receives the parent and a filenpath as parameters.
 * @param {object=} options optional parameters
 * @param {string=} options.label the label of the root node (default: '')
 * @returns {object} a tree structure as needed by [archy](https://www.npmjs.com/package/archy)
 * @access private
 */
export const treeFromPaths = (files, baseDir, renderLabelFn, options = {}) => {
  return {
    label: options.label || '',
    nodes: childNodesFromPaths(files, baseDir, renderLabelFn, files, baseDir),
  };
};

/**
 * Compute the child nodes of a node, given a list of paths
 * @param files
 * @param parent
 * @param renderLabelFn
 * @returns {Array}
 */
function childNodesFromPaths(files, parent, renderLabelFn, originalFiles, baseDir) {
  // Group by first path element
  const groups = groupBy(files, (file) => file.match(/^[^/]*\/?/));
  return Object.keys(groups).map((groupKey) => {
    const group = groups[groupKey];
    // Is this group explicitly part of the result, or
    // just implicit through its children
    const explicit = group.indexOf(groupKey) >= 0;
    let index = -1;
    if (explicit) {
      index = originalFiles.indexOf(parent.replace(baseDir, '') + groupKey);
    }

    return {
      label: renderLabelFn(parent, groupKey, explicit, explicit ? index : -1),
      nodes: childNodesFromPaths(
        // Remove parent directory from file paths
        group
          .map((node) => node.substr(groupKey.length))
          // Skip the empty path
          .filter((node) => node),
        // New parent..., normalize to one trailing slash
        parent + groupKey,
        renderLabelFn,
        originalFiles,
        baseDir,
      ),
    };
  });
}

export const newTreeFromPaths = (files, baseDir, renderLabelFn, options = {}, diffs) => {
  return options.label
    ? [
        {
          label: options.label,
          key: options.label,
          icon: '',
          children: newChildNodesFromPaths(files, baseDir, renderLabelFn, files, baseDir, diffs),
        },
      ]
    : newChildNodesFromPaths(files, baseDir, renderLabelFn, files, baseDir, diffs);
};

const newChildNodesFromPaths = (
  files,
  parent,
  renderLabelFn,
  originalFiles,
  baseDir,
  diffs,
  level = 1,
) => {
  // Group by first path element
  const groups = groupBy(files, (file) => file.match(/^[^/]*\/?/));
  return Object.keys(groups).map((groupKey) => {
    const group = groups[groupKey];
    // Is this group explicitly part of the result, or
    // just implicit through its children
    const explicit = group.indexOf(groupKey) >= 0;
    let index = -1;
    if (explicit) {
      index = originalFiles.indexOf(parent.replace(baseDir, '') + groupKey);
    }

    const value = renderLabelFn(parent, groupKey, explicit, explicit ? index : -1);
    let iconJsx = '';
    if (value.isFile) {
      iconJsx = <span>{renderIconHander(diffs, value)}</span>;
    }
    return {
      label: value.name?.replace('/', ''),
      key: value.isFile ? value.path : value.name + value.path,
      icon: iconJsx,
      children: newChildNodesFromPaths(
        // Remove parent directory from file paths
        group
          .map((node) => node.substr(groupKey.length))
          // Skip the empty path
          .filter((node) => node),
        // New parent..., normalize to one trailing slash
        parent + groupKey,
        renderLabelFn,
        originalFiles,
        baseDir,
        diffs,
        level + 1,
      ),
    };
  });
};

const renderIconHander = (diffs, path) => {
  const diffData = diffs.find((diff) => diff.new_path === path?.path);
  if (diffData.new_file) {
    return (
      <YunxiaoIcon
        size="small"
        type="additive-code-file-line"
        className="file-icon-add changes-tree-item-icon"
      />
    );
  } else if (diffData.deleted_file) {
    return (
      <YunxiaoIcon
        size="small"
        type="deleted-code-file-line"
        className="file-icon-delete changes-tree-item-icon"
      />
    );
  } else {
    return (
      <YunxiaoIcon
        size="small"
        type="modified-code-file-line"
        className="file-icon-modify changes-tree-item-icon"
      />
    );
  }
};
